属性动画原理
属性动画原理
相关类
ObjectAnimator 和 ValueAnimator 及区别
- ValueAnimator 类是先改变值,然后 手动赋值 给对象的属性从而实现动画;是 间接 对对象属性进行操作
- ObjectAnimator 类是先改变值,然后 自动赋值 给对象的属性 (反射调用对象的 setter 方法,还是需要手动 invalidate() 等操作的) 从而实现动画;是 直接 对对象属性进行操作;
Property
public abstract class Property<T, V> {
private final String mName;
private final Class<V> mType;
public static <T, V> Property<T, V> of(Class<T> hostType, Class<V> valueType, String name) {
return new ReflectiveProperty<T, V>(hostType, valueType, name);
}
public Property(Class<V> type, String name) {
mName = name;
mType = type;
}
public boolean isReadOnly() {
return false;
}
public void set(T object, V value) {
throw new UnsupportedOperationException("Property " + getName() +" is read-only");
}
public abstract V get(T object);
public String getName() {
return mName;
}
public Class<V> getType() {
return mType;
}
}
- set 方法的参数和 get 方法的返回值同一个 T 类型
- 在属性动画中,set 用于更新动画需要的值,动画更新(如调用 invalidate())
- 在属性动画中,get 用于使用者获取没有提供初始值
- 提供可包装三方 view 未能正确的实现 setter/getter 方法来实现 ObjectAnimator 属性动画
PropertyValuesHolder setter/getter; 计算值; 更新值
PropertyValuesHolder 作用
- 持有 Property 用于获取动画初始化值 get, 更新动画值 set;持有 mPropertyName 通过反射获取 getter/setter 方法,获取初始值和更新动画值
- 计算动画过程中的值(calculateValue),根据 fraction,具体通过 Keyframes.getValue()(int/float 是 IntKeyframesSet/FloatKeyFramesSet)
- 更新动画过程中的值(setAnimatedValue),调用 Property 的 set 或者 target.propertyName 的 setter 方法更新值
- 所有的属性动画的 value 都转化成 PropertyValuesHolder 来操作
public class PropertyValuesHolder {
// int类型默认估值器
private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
// float类型默认估值器
private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
String mPropertyName; // ofXXX传递进来的
protected Property mProperty; // ofXXX传递进来的
Method mSetter = null;
private Method mGetter = null;
Class mValueType;
Keyframes mKeyframes = null;
private TypeEvaluator mEvaluator;
private Object mAnimatedValue;
private TypeConverter mConverter;
private PropertyValuesHolder(String propertyName) {
mPropertyName = propertyName;
}
private PropertyValuesHolder(Property property) {
mProperty = property;
if (property != null) {
mPropertyName = property.getName();
}
}
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
return new IntPropertyValuesHolder(propertyName, values);
}
public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
return new IntPropertyValuesHolder(property, values);
}
// 反射获取getXXX方法(mPropertyName的首字母大写前加get,如foo,getFoo);如果有mConverter那么从mConverter中获取,否则从mValueType中获取
private void setupGetter(Class targetClass) {
mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
}
// 反射获取setXXX方法(mPropertyName的首字母大写前加set,如foo,setFoo);如果有mConverter那么从mConverter中获取,否则从mValueType中获取
void setupSetter(Class targetClass) {
Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
}
// 在ValueAnimator.startAnimation调用 → ObjectAnimator重写initAnimation调用(在ValueAnimator的initAnimation不会调用setupSetterAndGetter)
void setupSetterAndGetter(Object target) {
if (mProperty != null) { // mProperty不为null
try {
Object testValue = null;
List<Keyframe> keyframes = mKeyframes.getKeyframes(); // mKeyframes在ofXXX已经赋值了
int keyframeCount = keyframes == null ? 0 : keyframes.size();
for (int i = 0; i < keyframeCount; i++) {
Keyframe kf = keyframes.get(i);
if (!kf.hasValue() || kf.valueWasSetOnStart()) { // 未设置初始值(ofXXX只提供了一个值,Keyframe只有一个参数的构造方法) 或 设置了从gettter中获取值(这里是Propery.get)
if (testValue == null) {
testValue = convertBack(mProperty.get(target));
}
kf.setValue(testValue);
kf.setValueWasSetOnStart(true);
}
}
return;
} catch (ClassCastException e) { // Property和target类型对应不上
Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
") on target object " + target + ". Trying reflection instead");
mProperty = null;
}
}
// 没有写else是因为mProperty可能和target类型对应不上
if (mProperty == null) { // mProperty为null或mProperty可能和target类型对应不上被置为null了
Class targetClass = target.getClass();
if (mSetter == null) {
setupSetter(targetClass); // 注册setter
}
List<Keyframe> keyframes = mKeyframes.getKeyframes();
int keyframeCount = keyframes == null ? 0 : keyframes.size();
for (int i = 0; i < keyframeCount; i++) {
Keyframe kf = keyframes.get(i);
if (!kf.hasValue() || kf.valueWasSetOnStart()) { // 未设置初始值(ofXXX只提供了一个值,Keyframe只有一个参数的构造方法) 或 设置了从gettter中获取值
if (mGetter == null) {
setupGetter(targetClass);
if (mGetter == null) {
// Already logged the error - just return to avoid NPE
return;
}
}
try {
Object value = convertBack(mGetter.invoke(target));
kf.setValue(value);
kf.setValueWasSetOnStart(true);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
}
}
// 根据当前动画进度fraction计算具体的动画值;在ValueAnimator#animateValue调用
void calculateValue(float fraction) {
Object value = mKeyframes.getValue(fraction); // 调用Keyframes#getValue计算出具体的动画值
mAnimatedValue = mConverter == null ? value : mConverter.convert(value); // 如果设置了mConverter,转换下
}
// 更新值,在ObjectAnimator#animateValue中调用(animateValue是在ValueAnimator接受到时钟脉冲信号回调调用);mProperty和mSetter一般是互斥的,2种逻辑只会选择一种
void setAnimatedValue(Object target) {
if (mProperty != null) { // mProperty不为null,调用set更新值
mProperty.set(target, getAnimatedValue());
}
if (mSetter != null) {
try {
mTmpValueArray[0] = getAnimatedValue();
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
// ValueAnimator.initAnimation调用;自动处理Integer和Float的估值器,int和float不会走到这里,分别走IntKeyframeSet和FloatKeyframeSet
void init() {
if (mEvaluator == null) {
mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : (mValueType == Float.class) ? sFloatEvaluator : null;
}
if (mEvaluator != null) {
mKeyframes.setEvaluator(mEvaluator);
}
}
}
- init ValueAnimator.initAnimation 调用;自动处理 Integer 和 Float 的估值器,int 和 float 不会走到这里,分别走 IntKeyframeSet 和 FloatKeyframeSet
- setupSetterAndGetter 通过反射获取属性的 get set 方法或 Property 的 set/get 设置初始值(当然 一些 PropertyValuesHolder 的子类重载使用了 jni 方法获得反射方法。其实都是目的都是一样。)
- 更新值,先通过 calculateValue 计算出动画需要的值,再通过 setAnimatedValue 调用 Property#set 或反射 target 的 setter 更新值
Keyframe/Keyframes/KeyframeSet
关键帧的作用:
- 关键帧 keyframe 用于保存动画的每一个关键数据帧,个数由传入的 value 个数决定,系数 fraction 由 value 传入顺序决定。
- 保存每个关键帧的 fraction
- 通过动画类传入的 fraction,利用估值器计算出 真正的属性值。
Keyframe 关键帧
记录关键帧数据,他默认有三个实现类 IntKeyframe、FloatKeyframe、ObjectKeyframe,当然我们也能继承 Keyframe 实现自己的关键帧类型。不过大部分情况,提供的这三种方式已经够用了。
Keyframe 有三个重要的属性值:
- mHasValue 用于记录关键帧是否初始化了值,以 子类 IntKeyframe 为例,它有两个构造方法:
// Keyframe#IntKeyframe Android29
static class IntKeyframe extends Keyframe {
/**
* The value of the animation at the time mFraction.
*/
int mValue;
IntKeyframe(float fraction, int value) { // #1
mFraction = fraction;
mValue = value;
mValueType = int.class;
mHasValue = true;
}
IntKeyframe(float fraction) { // #2
mFraction = fraction;
mValueType = int.class;
}
}
第一个构造方法 (1) 初始化了 mValue ,同时 mHasValue 会标记 为 true;第二个构造方法 (2) 只初始化了 mFraction,并未给 mValue 赋值,默认 mHasValue 为 false;假如我们使用了 mHasValue 为 false 的关键帧。那么在动画初始化时会调用 PropertyValuesHolder.setupSetterAndGetter 给每一个关键帧赋初始值。
- mFraction 记录该关键帧所有关键帧中的位置,float 类型,值范围 0 - 1
- mValue 记录 value 值,当然不同类型的 Keyframe value 值类型也不同,所以 mValue 由子类实现
Keyframes 一堆 Keyframe 组成的集合
它的实现类有 KeyframeSet、PathKeyframes,它的子接口有 IntKeyframes、FloatKeyframes。
// Keyframes Android29
public interface Keyframes {
// 估值器,计算动画值时用;不是IntKeyframes和FloatKeyframes需要设置估值器
void setEvaluator(TypeEvaluator evaluator);
Class getType();
// 获取动画值;随着动画的完成度,interpolator和evaluator计算出出来的真正的动画值
Object getValue(float fraction);
List<Keyframe> getKeyframes();
}
KeyframeSet
两个子类 IntKeyframeSet、FloatKeyframeSet,分别实现了对应的 IntKeyframes 和 FloatKeyframes 接口。
ofInt 用的是 IntKeyframeSet,float 用的是 FloatKeyframeSet
// KeyframeSet Android29
public class KeyframeSet implements Keyframes {
int mNumKeyframes; // 关键帧数量
Keyframe mFirstKeyframe; // 第一帧
Keyframe mLastKeyframe; // 最后一帧
TimeInterpolator mInterpolator; // only used in the 2-keyframe case;插值器,只有数量2的关键帧用这个TimeInterpolator
List<Keyframe> mKeyframes; // only used when there are not 2 keyframes,不是数量2关键帧
TypeEvaluator mEvaluator; // 估值器
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
// immutable list
mKeyframes = Arrays.asList(keyframes);
mFirstKeyframe = keyframes[0];
mLastKeyframe = keyframes[mNumKeyframes - 1];
mInterpolator = mLastKeyframe.getInterpolator();
}
public static KeyframeSet ofInt(int... values) {
int numKeyframes = values.length;
IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
} else {
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
keyframes[i] =
(IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
}
}
return new IntKeyframeSet(keyframes);
}
// ofFloat
// ofKeyframe
// ofObject
// ofPath
public Object getValue(float fraction) {
// Special-case optimization for the common case of only two keyframes
if (mNumKeyframes == 2) { // 关键帧数量为2
if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction); // 插值器计算出fraction
}
return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
mLastKeyframe.getValue()); // 估值器算出真正的动画值
}
if (fraction <= 0f) {
final Keyframe nextKeyframe = mKeyframes.get(1);
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
final float prevFraction = mFirstKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(nextKeyframe.getFraction() - prevFraction);
return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
nextKeyframe.getValue());
} else if (fraction >= 1f) {
final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
final TimeInterpolator interpolator = mLastKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
final float prevFraction = prevKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(mLastKeyframe.getFraction() - prevFraction);
return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
mLastKeyframe.getValue());
}
Keyframe prevKeyframe = mFirstKeyframe;
for (int i = 1; i < mNumKeyframes; ++i) {
Keyframe nextKeyframe = mKeyframes.get(i);
if (fraction < nextKeyframe.getFraction()) {
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
final float prevFraction = prevKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(nextKeyframe.getFraction() - prevFraction);
// Apply interpolator on the proportional duration.
if (interpolator != null) {
intervalFraction = interpolator.getInterpolation(intervalFraction);
}
return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
nextKeyframe.getValue());
}
prevKeyframe = nextKeyframe;
}
// shouldn't reach here
return mLastKeyframe.getValue();
}
}
Keyframes 最核心的方法是 getValue 方法。
- 仅有两个关键帧时,只有在这种情况下才使用 mInterpolator- 插值器。通过插值器 得到修正后的系数 fraction,然后拿这个系数,通过估值器 mEvaluator 计算出属性值
- if (fraction <= 0f) 和 if (fraction >= 1f) 这两个分支是特殊情况,出现在动画第一帧和最后一帧的情况
- 正常动画中的分支,通过 fraction 查找符合 fraction < nextKeyframe.getFraction() 的第一帧,然后计算 fraction 在前一帧到当前帧范围内的位置。
例如查找到符合条件的当前帧 fraction =0.5; 前一帧 fraction = 0.2, 动画当前的 fraction =0.3,那么新的 intervalFraction = 1/3f。然后拿着这个值给估值器计算属性值。
TimeInterpolator 插值器
插值器,定义了动画的变化速率 (消逝到篡改的一个映射),允许动画有非线性的变化,如加速、减速
// TimeInterpolator Android29
public interface TimeInterpolator {
// elapsed fraction(消逝的部分)和 interpolated fraction(篡改的部分)的一个映射
float getInterpolation(float input);
// 参数input,[0~1],0表示开始,1表示结束
// 返回值,可能大于1表示overshoot,小于0表示undershoot
}
TypeEvaluator 估值器
估值器,动画变化过程中根据当前进度 fraction 计算出的做动画的实际值
public interface TypeEvaluator<T> {
public T evaluate(float fraction, T startValue, T endValue);
// 参数fraction 当前动画进度
// 参数startValue 开始值
// 参数endValue 结束值
// 返回值 简单的计算公式:result = startValue + fraction * (endValue - startValue)
}
属性动画原理 以 ofInt 举例
动画的创建
常用的属性动画创建方法
ObjectAnimator.ofInt()
ObjectAnimator.ofFloat()
ObjectAnimator.ofArgb()
ObjectAnimator.ofObject()
ObjectAnimator.ofPropertyValuesHolder()
以一个常用的方法 ObjectAnimator.ofInt() 为切入点层层深入解析属性动画的创建过程。
早期版本的属性动画 ofInt 方法有两个实现,到 Android O 为止已经有 4 个实现了,主要就是增加了 Path 参数:
// ObjectAnimator Android29
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName); // #1
anim.setIntValues(values); // #2
return anim;
}
public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values) {
ObjectAnimator anim = new ObjectAnimator(target, property); // #1
anim.setIntValues(values); // #2
return anim;
}
ofInt 方法的第一行 (propertyName1) 均是创建 ObjectAnimator 对象。但分别使用了两种构造方式 propertyName 和 Property。
ObjectAnimator 构造
// ObjectAnimator Android29
public ObjectAnimator() {}
private ObjectAnimator(Object target, String propertyName) {
setTarget(target); #propertyName#1
setPropertyName(propertyName); #propertyName#2
}
private <T> ObjectAnimator(T target, Property<T, ?> property) {
setTarget(target); #Property#1
setProperty(property); #Property#2
}
另外两个构造方法第一行代码都是 setTaret:
setTaret(Object)
private WeakReference<Object> mTarget;
public void setTarget(@Nullable Object target) {
final Object oldTarget = getTarget();
if (oldTarget != target) {
if (isStarted()) {
cancel();
}
mTarget = target == null ? null : new WeakReference<Object>(target);
// New target should cause re-initialization prior to starting
mInitialized = false;
}
}
setTarget(target);
mTarget 是记录需要做动画的对象的虚引用,方便以后调用该对象 getter/setter;如果 mTarget 不存在时,在 animateValue 会 cancel 当前动画。
setPropertyName/setProperty
propertyName2,setPropertyName(String)
public void setPropertyName(@NonNull String propertyName) {
// mValues could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
mValues 肯定为 null,不会走 if 分支,赋值 mPropertyName;如果 mValues 不为空的情况下,会更新 mValues 的第一个元素的属性值,并更新对应的 map 集合
Property2,setProperty(Property)
public void setProperty(@NonNull Property property) {
// mValues could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setProperty(property);
mValuesMap.remove(oldName);
mValuesMap.put(mPropertyName, valuesHolder);
}
if (mProperty != null) {
mPropertyName = property.getName();
}
mProperty = property;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
同 setPropertyName(String),mPropertyName 从 property.getName 获取,赋值 mProperty
现在看 anim.setIntValues(values);(#2):
// ObjectAnimator Android29
public void setIntValues(int... values) {
if (mValues == null || mValues.length == 0) {
if (mProperty != null) {
setValues(PropertyValuesHolder.ofInt(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
}
} else {
super.setIntValues(values);
}
}
第一次调用 mValues 为 null 走入 if 流程,假如我们调用的 propertyName 参数的方法,会执行 setValues(PropertyValuesHolder.ofInt(mPropertyName, values));,反之执行 setValues(PropertyValuesHolder.ofInt(mProperty, values));
我们先看看 PropertyValuesHolder.ofInt(mProperty/propertyName, values):
// PropertyValuesHolder Android29
public class PropertyValuesHolder {
private PropertyValuesHolder(String propertyName) {
mPropertyName = propertyName;
}
private PropertyValuesHolder(Property property) {
mProperty = property;
if (property != null) {
mPropertyName = property.getName();
}
}
public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
return new IntPropertyValuesHolder(property, values);
}
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
return new IntPropertyValuesHolder(propertyName, values);
}
public void setIntValues(int... values) {
mValueType = int.class;
mKeyframes = KeyframeSet.ofInt(values);
}
}
static class IntPropertyValuesHolder extends PropertyValuesHolder {
public IntPropertyValuesHolder(String propertyName, int... values) {
super(propertyName);
setIntValues(values);
}
public IntPropertyValuesHolder(Property property, int... values) {
super(property);
setIntValues(values);
if (property instanceof IntProperty) {
mIntProperty = (IntProperty) mProperty;
}
}
@Override
public void setIntValues(int... values) {
super.setIntValues(values);
mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
}
}
PropertyValuesHolder#ofInt,创建一个 IntPropertyValuesHolder,保存 propertyName/Property,调用 PropertyValuesHolder#setIntValues,生成关键帧 (keyframe)。
ofInt
现在回到 ObjectAnimator.ofInt,看看 setIntValues()
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setIntValues(values);
return anim;
}
public void setValues(PropertyValuesHolder... values) {
int numValues = values.length;
mValues = values;
mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
for (int i = 0; i < numValues; ++i) {
PropertyValuesHolder valuesHolder = values[i];
mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
}
mInitialized = false;
}
public void setIntValues(int... values) {
if (mValues == null || mValues.length == 0) {
if (mProperty != null) {
setValues(PropertyValuesHolder.ofInt(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
}
} else {
super.setIntValues(values);
}
}
可以看到用 values 用单个 PropertyValuesHolder 包装了下,赋值给了 mValues
在上面 PropertyValuesHolder 知道 PropertyValuesHolder.ofInt 会生成关键帧 keyframes 信息
动画启动 start()
// ObjectAnimator Android29
public void start() {
AnimationHandler.getInstance().autoCancelBasedOn(this);
super.start();
}
// ValueAnimator Android29
public void start() {
start(false);
}
private void start(boolean playBackwards) {
mReversing = playBackwards;
// 动画的状态
mStarted = true;
mPaused = false;
mRunning = false;
// ...
addAnimationCallback(0);
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
// If there's no start delay, init the animation and notify start listeners right away
// to be consistent with the previous behavior. Otherwise, postpone this until the first
// frame after the start delay.
startAnimation();
if (mSeekFraction == -1) { // mSeekFraction为负数,没有seek,从0开始
// No seek, start at play time 0. Note that the reason we are not using fraction 0
// is because for animations with 0 duration, we want to be consistent with pre-N
// behavior: skip to the final value immediately.
setCurrentPlayTime(0); // 最终也是调用的setCurrentFraction
} else {
setCurrentFraction(mSeekFraction);
}
}
}
public void setCurrentFraction(float fraction) { //
initAnimation();
fraction = clampFraction(fraction);
// ...
mOverallFraction = fraction;
final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing);
animateValue(currentIterationFraction);
}
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction); //
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this); // 通知AnimatorUpdateListener值更新了
}
}
}
如果是 ObjectAnimator,AnimationHandler.getInstance().autoCancelBasedOn(this) 会 cancel 相同 Target 和相同属性的动画,AnimationHandler 实例在线程局部单例。autoCancelBasedOn(this) 会遍历 AnimationHandler 实例持有的所有未完成的 ValueAnimator 实例,cancel 掉符合条件的动画。
紧接着 super.start() 调用了 ValueAnimator.start()
- fraction 是什么?
动画完成度,如果没有设置重复,就是[01],如果设置了重复值为[0repeatCount+1]
private float clampFraction(float fraction) {
if (fraction < 0) {
fraction = 0;
} else if (mRepeatCount != INFINITE) {
fraction = Math.min(fraction, mRepeatCount + 1);
}
return fraction;
}
- 在 animateValue,通过插值器
TimeInterpolator根据当前 fraction,计算出一个真正的 fraction - 可以看到,调用了 start 了,通过 addAnimationCallback 添加了一个什么 Calllback,稍后看;通知 AnimatorUpdateListener 值更新了
现在看 addAnimationCallback(this)
// ValueAnimator Android29
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay); // 如果dalay大于0,callback 会在delay 时间后的下一个frame获得回调
}
public AnimationHandler getAnimationHandler() {
return AnimationHandler.getInstance();
}
通过 AnimationHandler 的 addAnimationFrameCallback 添加了个回调,AnimationHandler 是什么?
// AnimationHandler Android29
public class AnimationHandler {
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime()); // 处理当前帧
if (mAnimationCallbacks.size() > 0) { // 如果当前当前未结束,mAnimationCallbacks>0的,继续注册下一帧的callback。为什么还要注册一次呢?之前不是注册过一次了吗?难道Choreographer把这个callback 释放了:是在Choreographer#recycleCallbackLocked(callbacks);来回收整个链表中的节点记录。
getProvider().postFrameCallback(this);
}
}
};
public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
public static AnimationHandler getInstance() { // 线程单例,即主线程都共享该实例,所有的属性动画都通过这个来处理
if (sAnimatorHandler.get() == null) {
sAnimatorHandler.set(new AnimationHandler());
}
return sAnimatorHandler.get();
}
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);
}
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
if (delay > 0) {
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
}
}
private AnimationFrameCallbackProvider getProvider() {
if (mProvider == null) {
mProvider = new MyFrameCallbackProvider();
}
return mProvider;
}
}
如果是首次 getProvider().postFrameCallback(mFrameCallback);,getProvider() 是啥?
// AnimatorHandler Android29
private AnimationFrameCallbackProvider getProvider() {
if (mProvider == null) {
mProvider = new MyFrameCallbackProvider();
}
return mProvider;
}
创建了一个 MyFrameCallbackProvider 实例,MyFrameCallbackProvider 继承 AnimationFrameCallbackProvider。
// AnimatorHandler Android29
public interface AnimationFrameCallbackProvider {
void postFrameCallback(Choreographer.FrameCallback callback);
void postCommitCallback(Runnable runnable);
long getFrameTime();
long getFrameDelay();
void setFrameDelay(long delay);
}
AnimationFrameCallbackProvider 接口定义了一些回调接口,按照注释说明主要作用是 提高 ValueAnimator 的可测性,通过这个接口隔离,我们可以自定义 定时脉冲,而不用使用系统默认的 Choreographer,这样我们可以在测试中使用任意的时间间隔的定时脉冲.既然可以方便测试,那肯定有 API 来更改 Provider:那就是 setProvider。
// AnimatorHandler Android29
public void setProvider(AnimationFrameCallbackProvider provider) {
if (provider == null) {
mProvider = new MyFrameCallbackProvider();
} else {
mProvider = provider;
}
}
ValueAnimator 提供了一个 setProvider 通过自定义的 Provider 提供我们想要的任意时间间隔的回调,来更新动画。
最终走的是 MyFrameCallbackProvider 的 postFrameCallback(),现在我们看看 MyFrameCallbackProvider 是什么?
// MyFrameCallbackProvider Android29
private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
final Choreographer mChoreographer = Choreographer.getInstance();
@Override
public void postFrameCallback(Choreographer.FrameCallback callback) {
mChoreographer.postFrameCallback(callback);
}
@Override
public void postCommitCallback(Runnable runnable) {
mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null);
}
@Override
public long getFrameTime() {
return mChoreographer.getFrameTime();
}
@Override
public long getFrameDelay() {
return Choreographer.getFrameDelay();
}
@Override
public void setFrameDelay(long delay) {
Choreographer.setFrameDelay(delay);
}
}
MyFrameCallbackProvider.postFrameCallback() 调用了 Choreographer.postFrameCallback(),使用 vsync(垂直同步)来协调 View 的绘制和动画的执行间隔。最终回调到 AnimationFrameCallback 的 boolean doAnimationFrame(long frameTime); 方法,frameTime 的时间是 SystemClock#uptimeMillis(),返回 true 动画结束。
系统默认使用的
Choreographer做定时脉冲来协调 frame 的更新;Choreographer 实例也是线程局部单例的。
现在我们又回到了 ValueAnimator 的 doAnimationFrame 方法:
// ValueAnimator Android29
// 处理一帧的动画,frameTime,当前帧的SystemClock#uptimeMillis()时间
public final void doAnimationFrame(long frameTime) {
AnimationHandler handler = AnimationHandler.getInstance();
if (mLastFrameTime == 0) {
// First frame
//如果是动画的第一次回调,注册调整到下一个 frame 窗口 再执行。
handler.addOneShotCommitCallback(this);
if (mStartDelay > 0) {
startAnimation();
}
if (mSeekFraction < 0) {
mStartTime = frameTime;
} else {
long seekTime = (long) (getScaledDuration() * mSeekFraction);
mStartTime = frameTime - seekTime;
mSeekFraction = -1;
}
mStartTimeCommitted = false; // allow start time to be compensated for jank
}
mLastFrameTime = frameTime;
if (mPaused) {
mPauseTime = frameTime;
handler.removeCallback(this);
return;
} else if (mResumed) {
mResumed = false;
if (mPauseTime > 0) {
// Offset by the duration that the animation was paused
mStartTime += (frameTime - mPauseTime);
mStartTimeCommitted = false; // allow start time to be compensated for jank
}
handler.addOneShotCommitCallback(this);
}
// ... comments
final long currentTime = Math.max(frameTime, mStartTime);
boolean finished = animateBasedOnTime(currentTime); // 返回值finished标记是否动画已经执行完毕。如果最后一个关键帧(Keyframe)执行完毕,这里返回true,会执行endAnimation()做一些状态位复位和动画结束回调等等。
if (finished) {
endAnimation();
}
}
frameTime 是当前 frame 时间,调用 animateBasedOnTime(currentTime)
boolean animateBasedOnTime(long currentTime) {
boolean done = false;
if (mRunning) {
final long scaledDuration = getScaledDuration(); // 默认不scale,调用overrideDurationScale(float durationScale)进行设置scale
final float fraction = scaledDuration > 0 ?
(float)(currentTime - mStartTime) / scaledDuration : 1f;
final float lastFraction = mOverallFraction;
final boolean newIteration = (int) fraction > (int) lastFraction;
final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
(mRepeatCount != INFINITE);
if (scaledDuration == 0) {
// 0 duration animator, ignore the repeat count and skip to the end
done = true;
} else if (newIteration && !lastIterationFinished) {
// Time to repeat
if (mListeners != null) {
int numListeners = mListeners.size();
for (int i = 0; i < numListeners; ++i) {
mListeners.get(i).onAnimationRepeat(this);
}
}
} else if (lastIterationFinished) {
done = true;
}
mOverallFraction = clampFraction(fraction);
float currentIterationFraction = getCurrentIterationFraction(mOverallFraction);
animateValue(currentIterationFraction);
}
return done;
}
- currentTime 是 Choreographer 发出的计时脉冲时间,纳秒计时。
- fraction 根据 currentTime 计算 fraction(
currentTime-mStartTime/duration) 系数,即动画时间流逝比。 - 然后执行 ValueAnimator#animateValue(currentIterationFraction) 计算动画的在当前时间比例下属性动画的值;如果是 ObjectAnimator 还会降属性值设置该 Target。animateValue 方法被 ObjectAnimator 重载了。
ValueAnimator#animateValue
// ValueAnimator Android29
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
- 参数 fraction 这个时间流逝比系数是线性的。通过
mInterpolator.getInterpolation()计算出我们想要的 fraction; - 从前面可以知道 mValues 是 PropertyValuesHolder,遍历所有的
PropertyValuesHolder.calculateValue(fraction)使用这个系数 fraction 计算出mAnimatedValue,从前面的 PropertyValuesHolder 分析可以知道,最终是通过 Keyframes#getValue,估值器计算最终的动画值(如果是 int 为 IntKeyframeSet)。 - 最后 mUpdateListeners#onAnimationUpdate 通知更新
ObjectAnimator#animateValue
如果是 ObjectAnimator,重写了 animateValue 方法
// ObjectAnimator Android29
void animateValue(float fraction) {
final Object target = getTarget();
if (mTarget != null && target == null) {
// We lost the target reference, cancel and clean up. Note: we allow null target if the
/// target has never been set.
cancel();
return;
}
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setAnimatedValue(target);
}
}
// PropertyValuesHolder Android29
void setAnimatedValue(Object target) {
if (mProperty != null) {
mProperty.set(target, getAnimatedValue());
}
if (mSetter != null) {
mTmpValueArray[0] = getAnimatedValue();
mSetter.invoke(target, mTmpValueArray);
}
}
可以看到 ObjectAnimator 相比较于 ValueAnimator,调用了 ValueAnimator 的 animateValue 计算动画值和通知更新后,还调用了 PropertyValuesHolder#setAnimatedValue 更新到对应 target 的属性值 (调用 Property 的 set 或反射 target 的 setter 方法)
总结
- ObjectAnimator#autoCancelBasedOn:相同属性,相同 target 的动画,防止出现多个动画同时更新 Target 的属性,出现错乱。不过这一行为默认是关闭的,设置 ObjectAnimator.setAutoCancel(true) 来打开
- AnimationHandler.addAnimationFrameCallback 向 Choreographer 注册 Choreographer.FrameCallback 回调,通过该回调获得渲染时间脉冲的回调;通过系统的 vsync 垂直同步信号来协调 cpu,gpu 和渲染的时序;Choreographer 获得 vsync 信号后 根据 当前帧的纳秒来查找哪些 Choreographer.FrameCallback 会被执行。
- 执行 AnimationHandler.doAnimationFrame() 方法,开始真正的动画逻辑。
- ValueAnimator.animateBasedOnTime(time) 执行,通过 TimeInterpolator 计算最终的 时间流逝比 fraction,然后调用 PropertyValuesHolder.calculateValue(fraction) 计算属性的值,并回调 AnimatorUpdateListener.onAnimationUpdate() 方法。
- PropertyValuesHolder 调用 Keyframes.getIntValue(fraction),这中间又使用到估值器 TypeEvaluator 和 Keyframe 最终结算处我们需要的属性值。
- 然后 ObjectAnimator 调用 PropertyValuesHolder.setAnimatedValue(target) 来更新 target 的属性值。